package com.shareware.common.secure.rsa

import android.util.Base64
import com.shareware.common.utils.log
import java.security.KeyFactory
import java.security.PrivateKey
import java.security.PublicKey
import java.security.interfaces.RSAPrivateKey
import java.security.interfaces.RSAPublicKey
import java.security.spec.PKCS8EncodedKeySpec
import java.security.spec.X509EncodedKeySpec
import javax.crypto.Cipher

/**
 * desc: 功能描述
 * email: mobiledeveloper@qq.com
 */
class RsaEncrypt private constructor() {

    companion object {
        private const val TAG = "RsaEncrypt"
        private const val ALGORITHM_NAME = "RSA"
        private const val RSA_TRANSFORMATION = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding"
        private const val KEY_LENGTH = 2048

        fun encrypt(content: String, publicKey: String): String {
            return if (content.isNotEmpty() && publicKey.isNotEmpty()) {
                Base64.encodeToString(encrypt(content.toByteArray(),
                        getPublicKey(publicKey)), Base64.DEFAULT)
            } else {
                log(TAG, "param is null")
                ""
            }
        }

        fun decrypt(cipherText: String, privateKey: String): String {
            return if (cipherText.isNotEmpty() && privateKey.isNotEmpty()) {
                decrypt(Base64.decode(cipherText, Base64.DEFAULT),
                        getPrivateKey(privateKey)).toString(Charsets.UTF_8)
            } else {
                ""
            }
        }

        private fun encrypt(contentBytes: ByteArray, publicKey: PublicKey?): ByteArray {
            return if (contentBytes.isNotEmpty() && isPublicKeyLengthRight(publicKey as? RSAPublicKey)) {
                try {
                    val cipher = Cipher.getInstance(RSA_TRANSFORMATION)
                    cipher.init(Cipher.ENCRYPT_MODE, publicKey)
                    cipher.doFinal(contentBytes)
                } catch (ignore: Exception) {
                    byteArrayOf()
                }
            } else {
                byteArrayOf()
            }
        }

        private fun decrypt(cipherTextBytes: ByteArray, privateKey: PrivateKey?): ByteArray {
            return if (cipherTextBytes.isNotEmpty() && isPrivateKeyLengthRight(privateKey as? RSAPrivateKey)) {
                try {
                    val cipher = Cipher.getInstance(RSA_TRANSFORMATION)
                    cipher.init(Cipher.DECRYPT_MODE, privateKey)
                    cipher.doFinal(cipherTextBytes)
                } catch (ignore: Exception) {
                    byteArrayOf()
                }
            } else {
                byteArrayOf()
            }
        }

        private fun isPublicKeyLengthRight(publicKey: RSAPublicKey?): Boolean {
            return if (publicKey == null) {
                false
            } else {
                publicKey.modulus.bitLength() >= KEY_LENGTH
            }
        }

        private fun isPrivateKeyLengthRight(privateKey: RSAPrivateKey?): Boolean {
            return if (privateKey == null) {
                false
            } else {
                privateKey.modulus.bitLength() >= KEY_LENGTH
            }
        }

        private fun getPublicKey(base64Str: String): PublicKey? {
            return try {
                val keyString = Base64.decode(base64Str, Base64.DEFAULT)
                val keyFactory = KeyFactory.getInstance(ALGORITHM_NAME)
                val x509EncodedKeySpec = X509EncodedKeySpec(keyString)
                keyFactory.generatePublic(x509EncodedKeySpec)
            } catch (ignore: Exception) {
                log(TAG, "${ignore.message}")
                null
            }
        }

        private fun getPrivateKey(base64Str: String): PrivateKey? {
            return try {
                val keyString = Base64.decode(base64Str, Base64.DEFAULT)
                val keyFactory = KeyFactory.getInstance(ALGORITHM_NAME)
                val pkcS8EncodedKeySpec = PKCS8EncodedKeySpec(keyString)
                keyFactory.generatePrivate(pkcS8EncodedKeySpec)
            } catch (ignore: Exception) {
                log(TAG, "${ignore.message}")
                null
            }
        }
    }
}